package value_object.data;

import java.util.List;

public class Strip {
	
	public static final int SATELLITE_HEIGHT = 633 * 1000; //HS = 633 km
	public static final double IMCOMPRESSIBLE_TRANSITION_TIME = 2.5; //Dmin = 2.5 seconds
	public static final double MAX_SPEED_OF_ROTATION_OF_SATELLITE = 0.05; //Vr = 0.05 radians/s
	
	private int strip_index;
	private int request_index;
	private int twin_strip_index;
	private float strip_surface;
	private float strip_acquisition_duration;
	
	private End_Information end0;
	private End_Information end1;
	
	private int strip_direction;
	
	/**
	 * Default constructor
	 */
	public Strip() {
		super();
	}
		
	/**
	 * Constructor with parameters
	 * @param strip_index
	 * @param request_index
	 * @param strip_index_twin
	 * @param strip_surface
	 * @param strip_acquisition_duration
	 */
	public Strip(int strip_index, int request_index, int strip_index_twin,
		int strip_surface, float strip_acquisition_duration) 
	{
		super();
		this.strip_index = strip_index;
		this.request_index = request_index;
		this.twin_strip_index = strip_index_twin;
		this.strip_surface = strip_surface;
		this.strip_acquisition_duration = strip_acquisition_duration;
	}
		
 	//Getters and setters
 
	public int getStrip_index() {
		return strip_index;
	}
	
	public void setStrip_index(int strip_index) {
		this.strip_index = strip_index;
	}
	
	public int getRequest_index() {
		return request_index;
	}
	
	public void setRequest_index(int request_index) {
		this.request_index = request_index;
	}
	
	public int getTwin_strip_index() {
		return twin_strip_index;
	}
	
	public void setTwin_strip_index(int twin_strip_index) {
		this.twin_strip_index = twin_strip_index;
	}
	
	public float getStrip_surface() {
		return strip_surface;
	}
	
	public void setStrip_surface(float strip_surface) {
		this.strip_surface = strip_surface;
	}
	
	public float getStrip_acquisition_duration() {
		return strip_acquisition_duration;
	}
	
	public void setStrip_acquisition_duration(float strip_acquisition_duration) {
		this.strip_acquisition_duration = strip_acquisition_duration;
	}

	public End_Information getEnd0() {
		return end0;
	}

	public void setEnd0(End_Information end0) {
		this.end0 = end0;
	}

	public End_Information getEnd1() {
		return end1;
	}

	public void setEnd1(End_Information end1) {
		this.end1 = end1;
	}
	
	public End_Information getEnd(int index) {
		if (index == 0)
			return end0;
		return end1; //index == 1 
	}
	
	public void setEnd(End_Information end, int index) {
		if (index == 0)
			setEnd0(end);			
		else if (index == 1)
			setEnd1(end);
	}
	
	public int getStrip_direction() {
		return strip_direction;
	}
	
	public void setStrip_direction(int strip_direction) {
		this.strip_direction = strip_direction;
	}

	/**
	 * Calculate distance between two points
	 * 
	 * @param lastOfFirstEnd
	 * @param firstOfSecondEnd
	 * @return distance from the last of First End to the first of Second End
	 */
	private double caculateDistance(End_Information lastOfFirstEnd, End_Information firstOfSecondEnd) {
		double xTemp = lastOfFirstEnd.getEnd_x_coordinate() - firstOfSecondEnd.getEnd_x_coordinate();
		double yTemp = lastOfFirstEnd.getEnd_y_coordinate() - firstOfSecondEnd.getEnd_y_coordinate();
		return Math.sqrt(xTemp * xTemp + yTemp * yTemp);
	}	

	/**
	 * @param previousStrip
	 * @return distance between the current strip and previous strip
	 */
	private double getDistanceFrom(Strip previousStrip) {

		if (previousStrip.getStrip_direction() == 0) {
			if (this.getStrip_direction() == 0) {
				return caculateDistance(previousStrip.getEnd1(), this.getEnd0());
			}
			if (this.getStrip_direction() == 1) {
				return caculateDistance(previousStrip.getEnd1(), this.getEnd1());
			}
		}
		if (previousStrip.getStrip_direction() == 1) {
			if (this.getStrip_direction() == 0) {
				return caculateDistance(previousStrip.getEnd0(), this.getEnd0());
			}
			if (this.getStrip_direction() == 1) {
				return caculateDistance(previousStrip.getEnd0(), this.getEnd1());
			}
		}
		return 0;
	}
	
	/**
	 * Get rotation
	 * 
	 * @param previousStrip
	 * @return Rotation in radians of satellite
	 */
	private double getRotation(Strip previousStrip) {
		return 2 * Math.atan(getDistanceFrom(previousStrip) / (2 * SATELLITE_HEIGHT));
	}
	
	/**
	 * Get transition
	 * 
	 * @param previousStrip
	 * @return Time in seconds required for the transition between the end of the first and the first of the second
	 */
	private double getTransitionTimeFrom(Strip previousStrip) {
		return IMCOMPRESSIBLE_TRANSITION_TIME + (getRotation(previousStrip) / MAX_SPEED_OF_ROTATION_OF_SATELLITE);
	}
	
	/**
	 * @return minimum time of current strip
	 */
	
	private double getTmin() {
		if(getStrip_direction() == 0)
			return Math.max(this.getEnd0().getEnd_earliest_visibility(), this.getEnd1().getEnd_earliest_visibility() - this.getStrip_acquisition_duration());
		else
			return Math.max(this.getEnd1().getEnd_earliest_visibility(), this.getEnd0().getEnd_earliest_visibility() - this.getStrip_acquisition_duration());
	}
		
	/**
	 * @return maximum time of current strip
	 */
	private double getTmax() {
		if (this.getStrip_direction() == 0)
			return Math.max(this.getEnd0().getEnd_latest_visibility(), this.getEnd1().getEnd_latest_visibility() - this.getStrip_acquisition_duration());
		else
			return Math.max(this.getEnd1().getEnd_latest_visibility(), this.getEnd0().getEnd_latest_visibility() - this.getStrip_acquisition_duration());
	}
	
	/**
	 * @return minimum time updated of previous strip
	 */
	private double getTminUpdateOf(Strip aPreviousStrip) {
		return Math.max(this.getTmin(), aPreviousStrip.getTmin() + this.getTransitionTimeFrom(aPreviousStrip) + this.getStrip_acquisition_duration());
	}
	
	/**
	 * <p> Verify the expression: Tmin(k) <= t(k) <= Tmax(k)
	 * 
	 * @return true if the expression is validated, else returns false
	 */
	public boolean validateConstraint1() {
		//System.out.println("GetTmin = "+ getTmin() + " - GetTmax = "+getTmax());
		return getTmin() <= getTmax();
	}
	
	/**
	 * <p> Verifies the expression: (t(k1) + Du(k1) + Dt(k1, k2)) <= t(k2)
	 * 
	 * @param previousStrip is the previous strip (the first one)
	 * @return true if the expression is validated, else returns false
	 */
	public boolean validateConstraint2(Strip previousStrip) {
		System.out.println("getTminUpdateOf = "+ getTminUpdateOf(previousStrip) + " - GetTmin = "+getTmin());
		return getTminUpdateOf(previousStrip) <= getTmin(); 
	}
	
	/**
	 * Check if the current strip exists in a list of strips (in the solution).
	 * <p>
	 * !!!The current strip is the one which we want to add to the solution
	 * @param stripList a list of strips (in the solution)
	 * @return true if the current strip respects the constraint 3
	 */
	public boolean validateConstraint3(List<Strip> stripList) {
		for (Strip aStrip : stripList) {
			if (this.getStrip_index() == aStrip.getStrip_index())
				return false;
		}
		return true;
	}

	
	/**
	 * Check if the current strip has the same direction as his twin strip in a list of strip (in the solution).
	 * <p>
	 * !!!The current strip is the one which we want to add to the solution
	 * @param stripList a list of strips (in the solution)
	 * @return true if the current strip respects the constraint 4
	 */
	
	public boolean validateConstraint4(List<Strip> stripList) {
		//mono request has only 1 strip
		//stereo request has >= 2strips
		for (Strip aStrip : stripList) {
			if ((this.getRequest_index() == aStrip.getRequest_index()) //belong to the same stereo request
					&& (this.getTwin_strip_index() == aStrip.getStrip_index() || aStrip.getTwin_strip_index() == this.getStrip_index())  //belong to the same stereo request 
					&& (this.getStrip_direction() != aStrip.getStrip_direction())) {
				
				return false;
			}
		}
		return true;
	}
	
	/**
	 * @return the data in detail of the strip	
	 */
	@Override
	public String toString() {
		StringBuffer buffer = new StringBuffer();
		
		buffer.append(this.strip_index + "(strip-index)");
		buffer.append(" ");
		buffer.append(this.request_index + "(associated-request-index)");
		buffer.append(" ");
		buffer.append(0 + "(to-be-ignored)");
		buffer.append(" ");
		buffer.append(this.twin_strip_index + "(twin-strip-index)");
		
		buffer.append("\n");
		
		buffer.append(0 + "(to-be-ignored)");
		buffer.append(" ");
		buffer.append(0 + "(to-be-ignored)");
		
		buffer.append("\n");
		
		buffer.append(this.strip_surface + "(strip-useful-surface)");		
		buffer.append(" ");
		buffer.append(this.strip_acquisition_duration + "(strip-acquisition-duraton)");
		
		buffer.append("\n");

		buffer.append(this.end0.toString());
		
		buffer.append("\n");
		
		buffer.append(this.end1.toString());
		
		return buffer.toString();
	}
	
	/**
	 * @return the data of the strip
	 */
	public String toSimpleString() {
		StringBuffer buffer = new StringBuffer();
		
		buffer.append(this.strip_index);
		buffer.append(" ");
		buffer.append(this.request_index);
		buffer.append(" ");
		buffer.append(0);
		buffer.append(" ");
		buffer.append(this.twin_strip_index);
		
		buffer.append("\n");
		
		buffer.append(0 + " " + 0);
				
		buffer.append("\n");
		
		buffer.append(this.strip_surface);		
		buffer.append(" ");
		buffer.append(this.strip_acquisition_duration);
		
		buffer.append("\n");

		buffer.append(this.end0.toSimpleString());
		
		buffer.append("\n");
		
		buffer.append(this.end1.toSimpleString());
		
		return buffer.toString();
	}
}
